package com.example.tys.baseapplication.test;

import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Process;
import android.support.annotation.Nullable;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;

import com.example.tys.baseapplication.BuildConfig;
import com.example.tys.baseapplication.Constant;
import com.example.tys.baseapplication.MyApp;
import com.example.tys.baseapplication.util.BitmapUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import disklrucache.DiskLruCache;

/**
 * Created by tys on 2018/1/26.
 */

public class ImageCache {
    private static ImageCache ourInstance;
    private LruCache<String, Bitmap> memoryCache;
    private DiskLruCache diskLruCache;
    private Set<WeakReference<Bitmap>> reusedPool;
    private ReferenceQueue<Bitmap> ref;
    private Thread cleanBitmapThread;
    private boolean isShutDown;

    public static ImageCache getInstance() {
        if (ourInstance == null) {
            synchronized (ImageView.class) {
                if (ourInstance == null) {
                    ourInstance = new ImageCache();
                }
            }
        }
        return ourInstance;
    }

    private ImageCache() {
        init();
    }

    public void init() {
        reusedPool = Collections.synchronizedSet(new HashSet<WeakReference<Bitmap>>());
        ActivityManager am = (ActivityManager) MyApp.getInstance().getSystemService(Context.ACTIVITY_SERVICE);
        //获得程序最大内存 M
        assert am != null;
        int memoryClass = am.getMemoryClass();
        Log.i("---", "最大内存：" + memoryClass + "M");
        memoryCache = new LruCache<String, Bitmap>(memoryClass / 8 * 1024 * 1024) {
            @Override
            protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
                if (oldValue != null) {//1.put相同key挤出来  2。get()时复写了Created()  3.大小满了被驱赶出 时调用次方法
                    if (oldValue.isMutable()) {//从内存（LruCache）移除时将bitmap加入内存复用池
                        //LruBitmapPool 预见性， 通过提前设置阈值，来控制容量
                        //WeakReference 最大化利用了gc的触发频率
                        reusedPool.add(new WeakReference<>(oldValue, getRef()));//添加bitmap复用池
                    } else {
                        if (!oldValue.isRecycled())
                            oldValue.recycle();
                    }
                }
            }

            @Override
            protected int sizeOf(String key, Bitmap value) {
                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
                    return value.getAllocationByteCount();
                }
                return value.getByteCount();//getRowBytes() * getHeight()
            }
        };
        try {
            //valueCount个 表示一个key对应 valueCount个 文件
            File file = new File(Constant.BASE_FOLDR_PATH);
            diskLruCache = DiskLruCache.open(file, BuildConfig.VERSION_CODE,
                    1, 10 * 1024 * 1024);//1M
        } catch (IOException e) {
            Log.e("----", e.toString());
            e.printStackTrace();
        }
    }

    public void stopBitmapCleanThread() {
        if (cleanBitmapThread != null) {
            isShutDown = true;
            cleanBitmapThread.interrupt();
            try {
                cleanBitmapThread.join(5 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void putToMemoryCache(String key, Bitmap bitmap) {
        memoryCache.put(key, bitmap);
    }

    public Bitmap getBitmapFromMemory(String key) {
        return memoryCache.get(key);
    }

    public void putToDiskCache(String key, Bitmap bitmap) {
        DiskLruCache.Snapshot snapshot = null;
        OutputStream os = null;
        try {
            snapshot = diskLruCache.get(key);
            if (snapshot == null) {
                DiskLruCache.Editor edit = diskLruCache.edit(key);
                if (null != edit) {
//                    os = edit.newOutputStream(1);//valueCount 大于1时， put值时得同时放入2个val 要不然要报错
//                    bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);
                    os = edit.newOutputStream(0);
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);
                    edit.commit();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (snapshot != null)
                snapshot.close();
            if (os != null)
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

    public Bitmap getBitmapFromDisk(String key, @Nullable Bitmap reusable) {
        DiskLruCache.Snapshot snapshot = null;
        Bitmap bitmap = null;
        InputStream is = null;
        try {
            snapshot = diskLruCache.get(key);
            if (null == snapshot) {
                return null;
            }
            //获得文件输入流 读取bitmap
            is = snapshot.getInputStream(0);
            //为了能够被复用内存
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inMutable = true;
            options.inBitmap = reusable;//reusable复用的bitmap 得满足内存大小要求
            bitmap = BitmapFactory.decodeStream(is, null, options);
//            if (null != bitmap) {
//                memoryCache.put(key, bitmap);
//            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != snapshot) {
                snapshot.close();
            }
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return bitmap;
    }

    public void cleanDiskCache() {
        try {
            diskLruCache.delete();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void clearMemory() {
        memoryCache.evictAll();
    }

    private ReferenceQueue<? super Bitmap> getRef() {
        if (ref == null) {
            ref = new ReferenceQueue<Bitmap>();
            cleanBitmapThread = new Thread(new Runnable() {//8.0 bitmap 内存在native层，需要手动调用isRecycled
                @Override
                public void run() {//在不需要时需要停止改线程（interrupt），之所有没有用变量来控制，应为线程阻塞时，并不会去判断变量
                    while (!isShutDown) {
                        try {
                            Reference<? extends Bitmap> reference = ref.remove(); //阻塞
                            if (reference != null) {
                                Bitmap bitMap = reference.get();
                                if (bitMap != null && !bitMap.isRecycled())
                                    bitMap.recycle();
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }, "thread bitmap clean");
            cleanBitmapThread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
            cleanBitmapThread.start();
        }
        return ref;
    }

    /**
     * 从复用池找，可以内存复用的bitmap
     *
     * @param w
     * @param h
     * @param sampleSize
     * @param config
     * @return
     */
    public Bitmap getReuseBitMap(int w, int h, int sampleSize, Bitmap.Config config) {
        Iterator<WeakReference<Bitmap>> iterator = reusedPool.iterator();
        //迭代查找符合复用条件的Bitmap
        Bitmap reuseBitmap = null;
        while (iterator.hasNext()) { //要移除操作， 如果用for each就是ConcurrentModificationException
            Bitmap bitmap = iterator.next().get();
            if (null != bitmap) {
                if (BitmapUtil.isReusedBitmap(bitmap, w, h, sampleSize, config)) {
                    reuseBitmap = bitmap;
                    iterator.remove();//移除内存复用池， 如果不移除的话，下一次又使用该内存，显示错乱
                    break;
                }
            } else {
                iterator.remove();
            }
        }
        return reuseBitmap;
    }
}
